package com.anttribe.dbdocument;

import com.anttribe.components.db.metadata.Database;
import com.anttribe.components.db.metadata.DbSchema;
import com.anttribe.components.db.metadata.DbTable;
import com.anttribe.components.db.sql.SqlOperation;
import com.anttribe.components.db.sql.SqlStatementFactory;
import com.anttribe.components.db.sql.format.SqlFormatter;
import com.anttribe.components.db.sql.statement.CreateTableSqlStatement;
import com.anttribe.components.db.supplier.DatabaseSupplier;
import com.anttribe.components.db.supplier.DatabaseSupplierFactory;
import com.anttribe.dbdocument.config.Configuration;
import com.anttribe.dbdocument.config.DBDataProcessorConfiguration;
import com.anttribe.dbdocument.constants.Constants;
import com.anttribe.dbdocument.exception.DbDocumentGeneratorException;
import com.anttribe.dbdocument.mapper.DbTableMapper;
import com.anttribe.dbdocument.model.DbTableExt;
import com.anttribe.docgenerator.DocGenerator;
import com.anttribe.docgenerator.in.DataModel;
import com.anttribe.docgenerator.out.Output;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author zhaoyong
 * @date 2021/10/5 0005
 */
@Slf4j
public class DbDocumentGenerator {

    /**
     * 配置项
     */
    private Configuration configuration;

    /**
     * 文档生成器
     */
    private DocGenerator docGenerator;

    public DbDocumentGenerator() {
        this(Configuration.DEFAULT_CONFIGURATION);
    }

    public DbDocumentGenerator(Configuration configuration) {
        this.configuration = configuration;
        this.docGenerator = new DocGenerator(this.configuration.getDocGeneratorConfiguration());
    }

    /**
     * 给定数据源信息和数据库信息，生成数据库文档
     *
     * @param dataSource DataSource
     * @return Output
     */
    public Output generate(DataSource dataSource) {
        return this.generate(dataSource, new HashMap<String, Object>());
    }

    /**
     * 给定数据源信息和数据库信息，生成数据库文档
     *
     * @param dataSource DataSource
     * @param extraDatas 扩展参数
     * @return Output
     */
    public Output generate(DataSource dataSource, Map<String, Object> extraDatas) {
        // 参数校验
        Optional.ofNullable(dataSource).orElseThrow(() -> new DbDocumentGeneratorException("dataSource must not be null"));

        // 构造DataModel
        DataModel dataModel = new DBDataProcessor(dataSource, this.configuration.getDbDataProcessorConfiguration())
                .process();
        if (null == dataModel || MapUtils.isEmpty(dataModel.getModelMap())) {
            log.error("can not process db data");
            return null;
        }

        // 添加额外数据
        if (!MapUtils.isEmpty(extraDatas)) {
            dataModel.addAllObjects(extraDatas);
        }

        // 生成文档
        return docGenerator.generate(dataModel);
    }

    /**
     * 数据库数据处理
     */
    class DBDataProcessor {

        /**
         * 数据源对象
         */
        private DataSource dataSource;

        /**
         * 数据处理配置
         */
        private DBDataProcessorConfiguration dbDataProcessorConfiguration;

        public DBDataProcessor(DataSource dataSource,
                               DBDataProcessorConfiguration dbDataProcessorConfiguration) {
            Optional.ofNullable(dataSource).orElseThrow(() -> new DbDocumentGeneratorException("dataSource must not be null"));
            Optional.ofNullable(dbDataProcessorConfiguration).orElseThrow(() -> new DbDocumentGeneratorException("dbDataProcessorConfiguration must not be null"));

            this.dataSource = dataSource;
            this.dbDataProcessorConfiguration = dbDataProcessorConfiguration;
        }

        /**
         * 处理数据，获取数据模型
         *
         * @return DataModel
         */
        public DataModel process() {
            DatabaseSupplier supplier = DatabaseSupplierFactory.me().getDatabaseSupplier(this.dataSource);
            if (null == supplier) {
                return DataModel.EMPTY;
            }

            DbSchema dbSchema = supplier.getDbSchema();
            if (null == dbSchema) {
                log.error("failed to get db schema info");
                throw new DbDocumentGeneratorException("can not get db schema");
            }

            // 获取数据库相关信息
            Database database = supplier.getDatabase();
            // 数据库表
            List<DbTable> dbTables = supplier.getTables();
            List<DbTableExt> dbTableExts = dbTables.stream().map(dbTable -> {
                DbTableExt dbTableExt = DbTableMapper.INSTANCE.toDbTableExt(dbTable);
                CreateTableSqlStatement sqlStatement = (CreateTableSqlStatement) SqlStatementFactory.me().createSqlStatement(SqlOperation.CREATE_TABLE, supplier.getDialect());
                sqlStatement.setDbTable(dbTable);
                dbTableExt.setCreateTableSql(sqlStatement.generateSql(SqlFormatter.FormatOptions.builder().prettyFormat(Boolean.TRUE).build()));
                return dbTableExt;
            }).collect(Collectors.toList());

            // 构造DataModel
            DataModel dataModel = new DataModel();
            dataModel.addObject(Constants.DbDataKey.KEY_DATABASE, database);
            dataModel.addObject(Constants.DbDataKey.KEY_DBSCHEMA, dbSchema);
            dataModel.addObject(Constants.DbDataKey.KEY_DBTABLES, dbTableExts);
            return dataModel;
        }

    }

}
